home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 May / may_2001.iso / intercd / root / Multimedia / ^DivX_Article / virtualdub / VirtualDub-source-1_4d / F_levels.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-03-20  |  21.1 KB  |  814 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2001 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include <stdio.h>
  19. #include <crtdbg.h>
  20. #include <math.h>
  21.  
  22. #include <windows.h>
  23. #include <commctrl.h>
  24.  
  25. #include "ScriptInterpreter.h"
  26. #include "ScriptValue.h"
  27. #include "ScriptError.h"
  28. #include "LevelControl.h"
  29.  
  30. #include "resource.h"
  31. #include "filter.h"
  32. #include "gui.h"
  33. #include "cpuaccel.h"
  34.  
  35. /////////////////////////////////////////////////////////////////////
  36.  
  37. extern HINSTANCE g_hInst;
  38.  
  39. typedef struct LevelsFilterData {
  40.     unsigned char xtblmono[256];
  41.     int xtblluma[256];
  42.  
  43.     int            iInputLo, iInputHi;
  44.     int            iOutputLo, iOutputHi;
  45.     double        rHalfPt, rGammaCorr;
  46.  
  47.     IFilterPreview *ifp;
  48.     RECT        rHisto;
  49.     long *        pHisto;
  50.     long        lHistoMax;
  51.     bool        fInhibitUpdate;
  52.     bool        bLuma;
  53. } LevelsFilterData;
  54.  
  55. /////////////////////////////////////////////////////////////////////
  56.  
  57. static int levels_init(FilterActivation *fa, const FilterFunctions *ff) {
  58.     LevelsFilterData *mfd = (LevelsFilterData *)fa->filter_data;
  59.  
  60.     mfd->iInputLo = 0x0000;
  61.     mfd->rHalfPt = 0.5;
  62.     mfd->rGammaCorr = 1.0;
  63.     mfd->iInputHi = 0xFFFF;
  64.     mfd->iOutputHi = 0xFFFF;
  65.     mfd->bLuma = true;
  66.  
  67.     return 0;
  68. }
  69.  
  70. /////////////////////////////////////////////////////////////////////
  71.  
  72. static int bright_table_R[256];
  73. static int bright_table_G[256];
  74. static int bright_table_B[256];
  75. extern "C" unsigned char YUV_clip_table[];
  76.  
  77. static void __declspec(naked) AsmLevelsRunScalar(Pixel32 *dst, PixOffset dstpitch, PixDim w, PixDim h, const int *xtblptr) {
  78.     __asm {
  79.             push    ebp
  80.             push    edi
  81.             push    esi
  82.             push    ebx
  83.             mov        edi,[esp+4+16]
  84.             mov        ebp,[esp+12+16]
  85.             shl        ebp,2
  86.             sub        dword ptr [esp+8+16],ebp
  87. yloop:
  88.             mov        ebp,[esp+12+16]
  89. xloop:
  90.             mov        eax,[edi]                    ;load source pixel
  91.             xor        ebx,ebx
  92.  
  93.             mov        bl,al
  94.             xor        ecx,ecx
  95.  
  96.             mov        cl,ah
  97.             and        eax,00ff0000h
  98.  
  99.             shr        eax,16
  100.             mov        edx,[bright_table_R+ebx*4]
  101.  
  102.             mov        esi,[bright_table_G+ecx*4]
  103.             add        edx,00008000h
  104.  
  105.             add        edx,esi
  106.             mov        esi,[bright_table_B+eax*4]
  107.  
  108.             add        edx,esi                        ;edx = bright*65536
  109.             mov        esi,[esp+20+16]                    ;load brightness translation table
  110.  
  111.             shr        edx,16                        ;edx = brightness
  112.             add        edi,4
  113.  
  114.             mov        edx,[esi+edx*4]                ;get brightness delta [AGI]
  115.  
  116.             mov        al,[YUV_clip_table+eax+edx+256]    ;[AGI]
  117.             mov        bl,[YUV_clip_table+ebx+edx+256]
  118.  
  119.             mov        cl,[YUV_clip_table+ecx+edx+256]
  120.             mov        [edi-2],al
  121.  
  122.             mov        [edi-4],bl
  123.             dec        ebp
  124.  
  125.             mov        [edi-3],cl
  126.             jne        xloop
  127.  
  128.             add        edi,[esp+8+16]
  129.  
  130.             dec        dword ptr [esp+16+16]
  131.             jne        yloop
  132.  
  133.             pop        ebx
  134.             pop        esi
  135.             pop        edi
  136.             pop        ebp
  137.             ret
  138.     }
  139. }
  140.  
  141. static void __declspec(naked) AsmLevelsRunMMX(Pixel32 *dst, PixOffset dstpitch, PixDim w, PixDim h, const int *xtblptr) {
  142.     static const __int64 bright_coeff=0x000026464b220e98i64;
  143.     static const __int64 round = 0x0000000000004000i64;
  144.     __asm {
  145.             push    ebp
  146.             push    edi
  147.             push    esi
  148.             push    ebx
  149.             mov        edi,[esp+4+16]
  150.             mov        ebp,[esp+12+16]
  151.             shl        ebp,2
  152.             sub        dword ptr [esp+8+16],ebp
  153.             mov        esi,[esp+20+16]
  154.  
  155.             movq        mm6,bright_coeff
  156. yloop:
  157.             mov            ebp,[esp+12+16]
  158.             dec            ebp
  159.             jz            do_single
  160. xloop:
  161.             movd        mm2,[edi]
  162.             pxor        mm7,mm7
  163.  
  164.             movd        mm3,[edi+4]
  165.             movq        mm0,mm6
  166.  
  167.             movq        mm1,round
  168.             punpcklbw    mm2,mm7
  169.  
  170.             pmaddwd        mm0,mm2
  171.             punpcklbw    mm3,mm7
  172.  
  173.             movq        mm4,mm3
  174.             pmaddwd        mm3,mm6
  175.  
  176.             movq        mm5,mm1
  177.             ;
  178.  
  179.             paddd        mm1,mm0
  180.             psrlq        mm0,32
  181.  
  182.             paddd        mm5,mm3
  183.             psrlq        mm3,32
  184.  
  185.             paddd        mm0,mm1
  186.             paddd        mm3,mm5
  187.  
  188.             psrld        mm0,15
  189.  
  190.             psrld        mm3,15
  191.  
  192.             movd        eax,mm0
  193.  
  194.             movd        ebx,mm3
  195.  
  196.             movd        mm1,[esi+eax*4]
  197.  
  198.             movd        mm5,[esi+ebx*4]
  199.             punpckldq    mm1,mm1
  200.  
  201.             paddw        mm2,mm1
  202.             punpckldq    mm5,mm5
  203.  
  204.             paddw        mm4,mm5
  205.             add            edi,8
  206.  
  207.             packuswb    mm2,mm4
  208.             sub            ebp,2
  209.  
  210.             ;
  211.             ;
  212.  
  213.             movq        [edi-8],mm2
  214.             ja            xloop
  215.             jnz            no_single
  216.  
  217.             ;----------
  218.  
  219. do_single:
  220.             movd        mm2,[edi]
  221.             movq        mm0,mm6
  222.             movq        mm1,round
  223.             pmaddwd        mm0,mm2
  224.             paddd        mm1,mm0
  225.             psrlq        mm0,32
  226.             paddd        mm0,mm1
  227.             psrld        mm0,15
  228.             movd        eax,mm0
  229.             movd        mm1,[esi+eax*4]
  230.             punpckldq    mm1,mm1
  231.             paddw        mm2,mm1
  232.             packuswb    mm2,mm2
  233.             movd        [edi],mm2
  234. no_single:
  235.  
  236.             ;----------
  237.  
  238.             add        edi,[esp+8+16]
  239.  
  240.             dec        dword ptr [esp+16+16]
  241.             jne        yloop
  242.  
  243.             pop        ebx
  244.             pop        esi
  245.             pop        edi
  246.             pop        ebp
  247.             emms
  248.             ret
  249.     }
  250. }
  251.  
  252. static int levels_run(const FilterActivation *fa, const FilterFunctions *ff) {
  253.     const LevelsFilterData *mfd = (LevelsFilterData *)fa->filter_data;
  254.  
  255.     if (mfd->bLuma) {
  256.         if (CPUGetEnabledExtensions() & CPUF_SUPPORTS_MMX)
  257.             AsmLevelsRunMMX(fa->dst.data, fa->dst.pitch, fa->dst.w, fa->dst.h, mfd->xtblluma);
  258.         else
  259.             AsmLevelsRunScalar(fa->dst.data, fa->dst.pitch, fa->dst.w, fa->dst.h, mfd->xtblluma);
  260.     } else
  261.         fa->dst.BitBltXlat1(0, 0, &fa->src, 0, 0, -1, -1, mfd->xtblmono);
  262.  
  263.     return 0;
  264. }
  265.  
  266. /////////////////////////////////////////////////////////////////////
  267.  
  268. static long levels_param(FilterActivation *fa, const FilterFunctions *ff) {
  269.     LevelsFilterData *mfd = (LevelsFilterData *)fa->filter_data;
  270.  
  271.     fa->dst.offset = fa->src.offset;
  272.  
  273.     return 0;
  274. }
  275.  
  276. static void levelsButtonCallback(bool fNewState, void *pvData) {
  277.     HWND hdlg = (HWND)pvData;
  278.  
  279.     EnableWindow(GetDlgItem(hdlg, IDC_SAMPLE), fNewState);
  280.     EnableWindow(GetDlgItem(hdlg, IDC_SAMPLE_MULTIPLE), fNewState);
  281. }
  282.  
  283. static void levelsRedoTables(LevelsFilterData *mfd) {
  284.     int i;
  285.     const long        y_base        = mfd->iOutputLo,
  286.                     y_range        = mfd->iOutputHi - mfd->iOutputLo;
  287.     const double    x_lo        = mfd->iInputLo / (double)0xffff,
  288.                     x_hi        = mfd->iInputHi / (double)0xffff;
  289.  
  290.     for(i=0; i<256; i++) {
  291.         bright_table_R[i] = 19595*i;
  292.         bright_table_G[i] = 38470*i;
  293.         bright_table_B[i] =  7471*i;
  294.     }
  295.  
  296.     if (x_lo == x_hi)
  297.         for(i=0; i<256; i++)
  298.             mfd->xtblmono[i] = (unsigned char)((int)(0.5 + y_base + y_range * 0.5) >> 8);
  299.     else
  300.         for(i=0; i<256; i++) {
  301.             double y, x;
  302.  
  303.             x = i / 255.0;
  304.  
  305.             if (x < x_lo)
  306.                 mfd->xtblmono[i] = mfd->iOutputLo >> 8;
  307.             else if (x > x_hi)
  308.                 mfd->xtblmono[i] = mfd->iOutputHi >> 8;
  309.             else {
  310.                 y = pow((x - x_lo) / (x_hi - x_lo), 1.0/mfd->rGammaCorr);
  311.  
  312.                 mfd->xtblmono[i] = (unsigned char)((int)(0.5 + y_base + y_range * y) >> 8);
  313.             }
  314.         }
  315.  
  316.     if (mfd->bLuma) {
  317.         if (CPUGetEnabledExtensions() & CPUF_SUPPORTS_MMX)
  318.             for(i=0; i<256; i++)
  319.                 mfd->xtblluma[i] = (((int)mfd->xtblmono[i] - i)&0xffff) * 0x10001;
  320.         else
  321.             for(i=0; i<256; i++)
  322.                 mfd->xtblluma[i] = (int)mfd->xtblmono[i] - i;
  323.     }
  324. }
  325.  
  326. static void levelsSampleCallback(VFBitmap *src, long pos, long cnt, void *pv) {
  327.     LevelsFilterData *mfd = (LevelsFilterData *)pv;
  328.     long *pHisto = mfd->pHisto;
  329.  
  330.     src->Histogram(0, 0, -1, -1, pHisto, VFBitmap::HISTO_GRAY);
  331. }
  332.  
  333. static void levelsSampleDisplay(LevelsFilterData *mfd, HWND hdlg) {
  334.     int i;
  335.     long *pHisto = mfd->pHisto;
  336.  
  337.     if (mfd->lHistoMax < 0)
  338.         ShowWindow(GetDlgItem(hdlg, IDC_HISTOGRAM), SW_HIDE);
  339.  
  340.     mfd->lHistoMax = pHisto[0];
  341.  
  342.     for(i=1; i<256; i++)
  343.         if (pHisto[i] > mfd->lHistoMax)
  344.             mfd->lHistoMax = pHisto[i];
  345.  
  346.     InvalidateRect(hdlg, &mfd->rHisto, FALSE);
  347.     UpdateWindow(hdlg);
  348. }
  349.  
  350. static BOOL APIENTRY levelsDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam) {
  351.     LevelsFilterData *mfd = (struct LevelsFilterData *)GetWindowLong(hDlg, DWL_USER);
  352.     char buf[32];
  353.  
  354.     switch (message)
  355.     {
  356.         case WM_INITDIALOG:
  357.             {
  358.                 HWND hwndItem;
  359.  
  360.                 mfd = (struct LevelsFilterData *)lParam;
  361.                 SetWindowLong(hDlg, DWL_USER, lParam);
  362.  
  363.                 GetWindowRect(GetDlgItem(hDlg, IDC_HISTOGRAM), &mfd->rHisto);
  364.                 ScreenToClient(hDlg, (POINT *)&mfd->rHisto + 0);
  365.                 ScreenToClient(hDlg, (POINT *)&mfd->rHisto + 1);
  366.  
  367.                 mfd->fInhibitUpdate = false;
  368.  
  369.                 hwndItem = GetDlgItem(hDlg, IDC_INPUT_LEVELS);
  370.  
  371.                 SendMessage(hwndItem, VLCM_SETTABCOUNT, FALSE, 3);
  372.                 SendMessage(hwndItem, VLCM_SETTABCOLOR, MAKELONG(0, FALSE), 0x000000);
  373.                 SendMessage(hwndItem, VLCM_SETTABCOLOR, MAKELONG(1, FALSE), 0x808080);
  374.                 SendMessage(hwndItem, VLCM_SETTABCOLOR, MAKELONG(2, FALSE), 0xFFFFFF);
  375.                 SendMessage(hwndItem, VLCM_MOVETABPOS, MAKELONG(0, FALSE), mfd->iInputLo);
  376.                 SendMessage(hwndItem, VLCM_MOVETABPOS, MAKELONG(1, FALSE), (int)(mfd->iInputLo + (mfd->iInputHi-mfd->iInputLo)*mfd->rHalfPt));
  377.                 SendMessage(hwndItem, VLCM_MOVETABPOS, MAKELONG(2,  TRUE), mfd->iInputHi);
  378.                 SendMessage(hwndItem, VLCM_SETGRADIENT, 0x000000, 0xFFFFFF);
  379.  
  380.                 hwndItem = GetDlgItem(hDlg, IDC_OUTPUT_LEVELS);
  381.  
  382.                 SendMessage(hwndItem, VLCM_SETTABCOUNT, FALSE, 2);
  383.                 SendMessage(hwndItem, VLCM_SETTABCOLOR, MAKELONG(0, FALSE), 0x000000);
  384.                 SendMessage(hwndItem, VLCM_SETTABCOLOR, MAKELONG(1, FALSE), 0xFFFFFF);
  385.                 SendMessage(hwndItem, VLCM_MOVETABPOS, MAKELONG(0, FALSE), mfd->iOutputLo);
  386.                 SendMessage(hwndItem, VLCM_MOVETABPOS, MAKELONG(1,  TRUE), mfd->iOutputHi);
  387.                 SendMessage(hwndItem, VLCM_SETGRADIENT, 0x000000, 0xFFFFFF);
  388.  
  389.                 CheckDlgButton(hDlg, IDC_LUMA, mfd->bLuma ? BST_CHECKED : BST_UNCHECKED);
  390.  
  391.                 mfd->ifp->SetButtonCallback(levelsButtonCallback, (void *)hDlg);
  392.                 mfd->ifp->SetSampleCallback(levelsSampleCallback, (void *)mfd);
  393.                 mfd->ifp->InitButton(GetDlgItem(hDlg, IDC_PREVIEW));
  394.  
  395.             }
  396.             return (TRUE);
  397.  
  398.         case WM_COMMAND:
  399.             if (mfd->fInhibitUpdate)
  400.                 return TRUE;
  401.  
  402.             switch(LOWORD(wParam)) {
  403.             case IDOK:
  404.                 mfd->ifp->Close();
  405.                 EndDialog(hDlg, 0);
  406.                 return TRUE;
  407.  
  408.             case IDCANCEL:
  409.                 mfd->ifp->Close();
  410.                 EndDialog(hDlg, 1);
  411.                 return TRUE;
  412.  
  413.             case IDC_PREVIEW:
  414.                 mfd->ifp->Toggle(hDlg);
  415.                 return TRUE;
  416.  
  417.             case IDC_SAMPLE:
  418.                 memset(mfd->pHisto, 0, sizeof(mfd->pHisto[0])*256);
  419.                 mfd->ifp->SampleCurrentFrame();
  420.                 levelsSampleDisplay(mfd, hDlg);
  421.                 return TRUE;
  422.  
  423.             case IDC_SAMPLE_MULTIPLE:
  424.                 memset(mfd->pHisto, 0, sizeof(mfd->pHisto[0])*256);
  425.                 mfd->ifp->SampleFrames();
  426.                 levelsSampleDisplay(mfd, hDlg);
  427.                 return TRUE;
  428.  
  429.             case IDC_LUMA:
  430.                 {    
  431.                     bool bNewState = !!IsDlgButtonChecked(hDlg, IDC_LUMA);
  432.  
  433.                     if (bNewState != mfd->bLuma) {
  434.                         mfd->bLuma = bNewState;
  435.                         levelsRedoTables(mfd);
  436.                         mfd->ifp->RedoFrame();
  437.                     }
  438.  
  439.                 }
  440.                 return TRUE;
  441.  
  442.             case IDC_INPUTGAMMA:
  443.                 mfd->fInhibitUpdate = true;
  444.                 if (HIWORD(wParam) == EN_CHANGE) {
  445.                     double rv;
  446.  
  447.                     if (GetWindowText((HWND)lParam, buf, sizeof buf))
  448.                         if (1 == sscanf(buf, "%lg", &rv)) {
  449.                             // pt^(1/rv) = 0.5
  450.                             // pt = 0.5^-(1/rv)
  451.  
  452.                             if (rv < 0.01)
  453.                                 rv = 0.01;
  454.                             else if (rv > 10.0)
  455.                                 rv = 10.0;
  456.  
  457.                             mfd->rGammaCorr = rv;
  458.                             mfd->rHalfPt = pow(0.5, rv);
  459.  
  460.                             _RPT4(0, "%g %g %4X %4X\n", mfd->rHalfPt, mfd->rGammaCorr, mfd->iInputLo, mfd->iInputHi);
  461.  
  462.                             SendDlgItemMessage(hDlg, IDC_INPUT_LEVELS, VLCM_SETTABPOS, MAKELONG(1, TRUE),
  463.                                     (int)(0.5 + mfd->iInputLo + (mfd->iInputHi - mfd->iInputLo)*mfd->rHalfPt));
  464.  
  465.                             levelsRedoTables(mfd);
  466.                             mfd->ifp->RedoFrame();
  467.                         }
  468.                 } else if (HIWORD(wParam) == EN_KILLFOCUS) {
  469.                     sprintf(buf, "%.3f", mfd->rGammaCorr);
  470.                     SetWindowText((HWND)lParam, buf);
  471.                 }
  472.                 mfd->fInhibitUpdate = false;
  473.                 return TRUE;
  474.  
  475.             case IDC_INPUTLO:
  476.                 mfd->fInhibitUpdate = true;
  477.                 if (HIWORD(wParam) == EN_CHANGE) {
  478.                     BOOL f;
  479.                     int v;
  480.  
  481.                     v = GetDlgItemInt(hDlg, IDC_INPUTLO, &f, FALSE) * 0x0101;
  482.  
  483.                     if (v<0)
  484.                         v=0;
  485.                     else if (v>mfd->iInputHi)
  486.                         v = mfd->iInputHi;
  487.  
  488.                     mfd->iInputLo = v;
  489.  
  490.                     SendDlgItemMessage(hDlg, IDC_INPUT_LEVELS, VLCM_SETTABPOS, MAKELONG(0, TRUE), v);
  491.                     SendDlgItemMessage(hDlg, IDC_INPUT_LEVELS, VLCM_SETTABPOS, MAKELONG(1, TRUE), (int)floor(0.5 + mfd->iInputLo + (mfd->iInputHi-mfd->iInputLo)*mfd->rHalfPt));
  492.                     levelsRedoTables(mfd);
  493.                     mfd->ifp->RedoFrame();
  494.                 } else if (HIWORD(wParam) == EN_KILLFOCUS)
  495.                     SetDlgItemInt(hDlg, IDC_INPUTLO, mfd->iInputLo>>8, FALSE);
  496.  
  497.                 mfd->fInhibitUpdate = false;
  498.                 return TRUE;
  499.             case IDC_INPUTHI:
  500.                 mfd->fInhibitUpdate = true;
  501.                 if (HIWORD(wParam) == EN_CHANGE) {
  502.                     BOOL f;
  503.                     int v;
  504.  
  505.                     v = GetDlgItemInt(hDlg, IDC_INPUTHI, &f, FALSE)*0x0101;
  506.  
  507.                     if (v<mfd->iInputLo)
  508.                         v=mfd->iInputLo;
  509.                     else if (v>0xffff)
  510.                         v = 0xffff;
  511.  
  512.                     mfd->iInputHi = v;
  513.  
  514.                     SendDlgItemMessage(hDlg, IDC_INPUT_LEVELS, VLCM_SETTABPOS, MAKELONG(2, TRUE), v);
  515.                     SendDlgItemMessage(hDlg, IDC_INPUT_LEVELS, VLCM_SETTABPOS, MAKELONG(1, TRUE), (int)floor(0.5 + mfd->iInputLo + (mfd->iInputHi-mfd->iInputLo)*mfd->rHalfPt));
  516.                     levelsRedoTables(mfd);
  517.                     mfd->ifp->RedoFrame();
  518.                 } else if (HIWORD(wParam) == EN_KILLFOCUS)
  519.                     SetDlgItemInt(hDlg, IDC_INPUTHI, mfd->iInputHi>>8, FALSE);
  520.  
  521.                 mfd->fInhibitUpdate = false;
  522.                 return TRUE;
  523.             case IDC_OUTPUTLO:
  524.                 mfd->fInhibitUpdate = true;
  525.                 if (HIWORD(wParam) == EN_CHANGE) {
  526.                     BOOL f;
  527.                     int v;
  528.  
  529.                     v = GetDlgItemInt(hDlg, IDC_OUTPUTLO, &f, FALSE)*0x0101;
  530.  
  531.                     if (v<0)
  532.                         v=0;
  533.                     else if (v>mfd->iOutputHi)
  534.                         v = mfd->iOutputHi;
  535.  
  536.                     mfd->iOutputLo = v;
  537.  
  538.                     SendDlgItemMessage(hDlg, IDC_OUTPUT_LEVELS, VLCM_SETTABPOS, MAKELONG(0, TRUE), v);
  539.  
  540.                     levelsRedoTables(mfd);
  541.                     mfd->ifp->RedoFrame();
  542.  
  543.                 } else if (HIWORD(wParam) == EN_KILLFOCUS)
  544.                     SetDlgItemInt(hDlg, IDC_OUTPUTLO, mfd->iOutputLo>>8, FALSE);
  545.  
  546.                 mfd->fInhibitUpdate = false;
  547.                 return TRUE;
  548.             case IDC_OUTPUTHI:
  549.                 mfd->fInhibitUpdate = true;
  550.                 if (HIWORD(wParam) == EN_CHANGE) {
  551.                     BOOL f;
  552.                     int v;
  553.  
  554.                     v = GetDlgItemInt(hDlg, IDC_OUTPUTHI, &f, FALSE)*0x0101;
  555.  
  556.                     if (v<mfd->iOutputLo)
  557.                         v=mfd->iOutputLo;
  558.                     else if (v>0xffff)
  559.                         v = 0xffff;
  560.  
  561.                     mfd->iOutputHi = v;
  562.  
  563.                     SendDlgItemMessage(hDlg, IDC_OUTPUT_LEVELS, VLCM_SETTABPOS, MAKELONG(1, TRUE), v);
  564.  
  565.                     levelsRedoTables(mfd);
  566.                     mfd->ifp->RedoFrame();
  567.                 } else if (HIWORD(wParam) == EN_KILLFOCUS)
  568.                     SetDlgItemInt(hDlg, IDC_OUTPUTHI, mfd->iOutputHi>>8, FALSE);
  569.  
  570.                 mfd->fInhibitUpdate = false;
  571.                 return TRUE;
  572.             }
  573.             break;
  574.  
  575.         case WM_PAINT:
  576.             if (mfd->lHistoMax < 0)
  577.                 return FALSE;
  578.             {
  579.                 HDC hdc;
  580.                 PAINTSTRUCT ps;
  581.                 RECT rPaint, rClip;
  582.                 int i;
  583.  
  584.                 hdc = BeginPaint(hDlg, &ps);
  585.  
  586.                 i = GetClipBox(hdc, &rClip);
  587.  
  588.                 if (i==ERROR || i==NULLREGION || IntersectRect(&rPaint, &mfd->rHisto, &rClip)) {
  589.                     int x, xlo, xhi, w;
  590.                     long lMax = mfd->lHistoMax;
  591.  
  592.                     w = mfd->rHisto.right - mfd->rHisto.left;
  593.  
  594.                     if (i==NULLREGION) {
  595.                         xlo = 0;
  596.                         xhi = w;
  597.                     } else {
  598.                         xlo = rPaint.left - mfd->rHisto.left;
  599.                         xhi = rPaint.right - mfd->rHisto.left;
  600.                     }
  601.  
  602.                     FillRect(hdc, &mfd->rHisto, (HBRUSH)GetStockObject(WHITE_BRUSH));
  603.  
  604.                     for(x=xlo; x<xhi; x++) {
  605.                         int xp, yp, h;
  606.                         long y;
  607.  
  608.                         i = (x * 0xFF00) / (w-1);
  609.  
  610.                         if (i >= 0xFF00)
  611.                             y = mfd->pHisto[255];
  612.                         else
  613.                             y = (long)(((__int64)mfd->pHisto[(i>>8)+0] * (256 - (i&255)) + (__int64)mfd->pHisto[(i>>8)+1] * (i&255)) >> 8);
  614.  
  615.                         xp = x+mfd->rHisto.left;
  616.                         yp = mfd->rHisto.bottom-1;
  617.                         h = MulDiv(y, mfd->rHisto.bottom-mfd->rHisto.top, lMax);
  618.  
  619.                         if (h>0) {
  620.                             MoveToEx(hdc, x+mfd->rHisto.left, yp, NULL);
  621.                             LineTo(hdc, x+mfd->rHisto.left, yp - h);
  622.                         }
  623.                     }
  624.                 }
  625.  
  626.                 EndPaint(hDlg, &ps);
  627.             }
  628.             break;
  629.  
  630.         case WM_NOTIFY:
  631.             if (!mfd->fInhibitUpdate) {
  632.                 NMHDR *pnmh = (NMHDR *)lParam;
  633.                 NMVLTABCHANGE *pnmvltc = (NMVLTABCHANGE *)lParam;
  634.                 char buf[32];
  635.  
  636.                 mfd->fInhibitUpdate = true;
  637.  
  638.                 switch(pnmh->idFrom) {
  639.                 case IDC_INPUT_LEVELS:
  640.                     switch(pnmvltc->iTab) {
  641.                     case 0:
  642.                         mfd->iInputLo = pnmvltc->iNewPos;
  643.                         SetDlgItemInt(hDlg, IDC_INPUTLO, mfd->iInputLo>>8, FALSE);
  644.                         UpdateWindow(GetDlgItem(hDlg, IDC_INPUTLO));
  645.                         SendDlgItemMessage(hDlg, IDC_INPUT_LEVELS, VLCM_SETTABPOS, MAKELONG(1, TRUE), (int)floor(0.5 + mfd->iInputLo + (mfd->iInputHi-mfd->iInputLo)*mfd->rHalfPt));
  646.                         break;
  647.                     case 1:
  648.                         if (mfd->iInputLo == mfd->iInputHi) {
  649.                             mfd->rHalfPt = 0.5;
  650.                             mfd->rGammaCorr = 1.0;
  651.                         } else {
  652.  
  653.                             // compute halfpoint... if inputlo/hi range is even, drop down by 1/2 to allow for halfpoint
  654.  
  655.                             if ((mfd->iInputLo + mfd->iInputHi) & 1)
  656.                                 mfd->rHalfPt = ((pnmvltc->iNewPos<=(mfd->iInputLo+mfd->iInputHi)/2 ? +1 : -1) + 2*(pnmvltc->iNewPos  - mfd->iInputLo))
  657.                                         / (2.0*(mfd->iInputHi - mfd->iInputLo - 1));
  658.                             else
  659.                                 mfd->rHalfPt = (pnmvltc->iNewPos - mfd->iInputLo) / (double)(mfd->iInputHi - mfd->iInputLo);
  660.  
  661.                             // halfpt ^ (1/gc) = 0.5
  662.                             // 1/gc = log_halfpt(0.5)
  663.                             // 1/gc = log(0.5) / log(halfpt)
  664.                             // gc = log(halfpt) / log(0.5)
  665.  
  666.                             // clamp gc to [0.01...10.0]
  667.  
  668.                             if (mfd->rHalfPt > 0.9930925)
  669.                                 mfd->rHalfPt = 0.9930925;
  670.                             else if (mfd->rHalfPt < 0.0009765625)
  671.                                 mfd->rHalfPt = 0.0009765625;
  672.  
  673.                             mfd->rGammaCorr = log(mfd->rHalfPt) / -0.693147180559945309417232121458177;    // log(0.5);
  674.                         }
  675.  
  676.                         sprintf(buf, "%.3f", mfd->rGammaCorr);
  677.                         SetDlgItemText(hDlg, IDC_INPUTGAMMA, buf);
  678.                         UpdateWindow(GetDlgItem(hDlg, IDC_INPUTGAMMA));
  679.                         break;
  680.                     case 2:
  681.                         mfd->iInputHi = pnmvltc->iNewPos;
  682.                         SetDlgItemInt(hDlg, IDC_INPUTHI, mfd->iInputHi>>8, FALSE);
  683.                         UpdateWindow(GetDlgItem(hDlg, IDC_INPUTHI));
  684.                         SendDlgItemMessage(hDlg, IDC_INPUT_LEVELS, VLCM_SETTABPOS, MAKELONG(1, TRUE), (int)floor(0.5 + mfd->iInputLo + (mfd->iInputHi-mfd->iInputLo)*mfd->rHalfPt));
  685.                         break;
  686.                     }
  687.                     levelsRedoTables(mfd);
  688.                     mfd->ifp->RedoFrame();
  689.                     mfd->fInhibitUpdate = false;
  690.                     return TRUE;
  691.                 case IDC_OUTPUT_LEVELS:
  692.                     switch(pnmvltc->iTab) {
  693.                     case 0:
  694.                         mfd->iOutputLo = pnmvltc->iNewPos;
  695.                         SetDlgItemInt(hDlg, IDC_OUTPUTLO, (pnmvltc->iNewPos >> 8), FALSE);
  696.                         break;
  697.                     case 1:
  698.                         mfd->iOutputHi = pnmvltc->iNewPos;
  699.                         SetDlgItemInt(hDlg, IDC_OUTPUTHI, (pnmvltc->iNewPos >> 8), FALSE);
  700.                         break;
  701.                     }
  702.                     levelsRedoTables(mfd);
  703.                     mfd->ifp->RedoFrame();
  704.                     mfd->fInhibitUpdate = false;
  705.                     return TRUE;
  706.                 }
  707.                 mfd->fInhibitUpdate = false;
  708.             }
  709.             break;
  710.     }
  711.     return FALSE;
  712. }
  713.  
  714. static int levels_config(FilterActivation *fa, const FilterFunctions *ff, HWND hWnd) {
  715.     LevelsFilterData *mfd = (LevelsFilterData *)fa->filter_data;
  716.     LevelsFilterData mfd2 = *mfd;
  717.     int ret;
  718.     long histo[256];
  719.  
  720.     mfd->ifp = fa->ifp;
  721.     mfd->pHisto = histo;
  722.     mfd->lHistoMax = -1;
  723.  
  724.     ret = DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_FILTER_LEVELS), hWnd, levelsDlgProc, (LONG)mfd);
  725.  
  726.     if (ret)
  727.         *mfd = mfd2;
  728.  
  729.     return ret;
  730. }
  731.  
  732. ///////////////////////////////////////////
  733.  
  734. static int levels_start(FilterActivation *fa, const FilterFunctions *ff) {
  735.     LevelsFilterData *mfd = (LevelsFilterData *)fa->filter_data;
  736.  
  737.     levelsRedoTables(mfd);
  738.  
  739.     return 0;
  740. }
  741.  
  742. static void levels_string(const FilterActivation *fa, const FilterFunctions *ff, char *buf) {
  743.     LevelsFilterData *mfd = (LevelsFilterData *)fa->filter_data;
  744.  
  745.     sprintf(buf, " ( [%4.2f-%4.2f] > %.2f > [%4.2f-%4.2f] (%s) )",
  746.             mfd->iInputLo/(double)0xffff,
  747.             mfd->iInputHi/(double)0xffff,
  748.             mfd->rGammaCorr,
  749.             mfd->iOutputLo/(double)0xffff,
  750.             mfd->iOutputHi/(double)0xffff,
  751.             mfd->bLuma ? "Y" : "RGB"
  752.             );
  753. }
  754.  
  755. static void levels_script_config(IScriptInterpreter *isi, void *lpVoid, CScriptValue *argv, int argc) {
  756.     FilterActivation *fa = (FilterActivation *)lpVoid;
  757.     LevelsFilterData *mfd = (LevelsFilterData *)fa->filter_data;
  758.  
  759.     mfd->iInputLo    = argv[0].asInt();
  760.     mfd->iInputHi    = argv[1].asInt();
  761.     mfd->rGammaCorr    = argv[2].asInt() / 16777216.0;
  762.     mfd->rHalfPt    = pow(0.5, mfd->rGammaCorr);
  763.     mfd->iOutputLo    = argv[3].asInt();
  764.     mfd->iOutputHi    = argv[4].asInt();
  765.  
  766.     mfd->bLuma = false;
  767.     if (argc > 5)
  768.         mfd->bLuma = !!argv[5].asInt();
  769. }
  770.  
  771. static ScriptFunctionDef levels_func_defs[]={
  772.     { (ScriptFunctionPtr)levels_script_config, "Config", "0iiiii" },
  773.     { (ScriptFunctionPtr)levels_script_config, NULL, "0iiiiii" },
  774.     { NULL },
  775. };
  776.  
  777. static CScriptObject levels_obj={
  778.     NULL, levels_func_defs
  779. };
  780.  
  781. static bool levels_script_line(FilterActivation *fa, const FilterFunctions *ff, char *buf, int buflen) {
  782.     LevelsFilterData *mfd = (LevelsFilterData *)fa->filter_data;
  783.  
  784.     _snprintf(buf, buflen, "Config(0x%04X,0x%04X,0x%08lX,0x%04X,0x%04X)"
  785.                 ,mfd->iInputLo
  786.                 ,mfd->iInputHi
  787.                 ,(long)(0.5 + mfd->rGammaCorr * 16777216.0)
  788.                 ,mfd->iOutputLo
  789.                 ,mfd->iOutputHi
  790.                 );
  791.  
  792.     return true;
  793. }
  794.  
  795. FilterDefinition filterDef_levels={
  796.     0,0,NULL,
  797.     "levels",
  798.     "Applies a levels or levels-correction transform to the image."
  799.             "\n\n[Assembly optimized]"
  800.         ,
  801.     NULL,NULL,
  802.     sizeof(LevelsFilterData),
  803.     levels_init,NULL,
  804.     levels_run,
  805.     levels_param,
  806.     levels_config,
  807.     levels_string,
  808.     levels_start,
  809.     NULL,                    // end
  810.  
  811.     &levels_obj,
  812.     levels_script_line,
  813. };
  814.